package org.dromara.system.kernel.channel;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.JSONWriter;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.encrypt.utils.EncryptUtils;
import org.dromara.system.domain.PayAccount;
import org.dromara.system.domain.PayGroupAccount;
import org.dromara.system.domain.PayOrder;
import org.dromara.system.domain.PayOrderLog;
import org.dromara.system.domain.bo.aparams.AOrderBo;
import org.dromara.system.domain.bo.companychannel2.CpCh2PushBo;
import org.dromara.system.domain.bo.companychannel2.CpCh2UpdateBo;
import org.dromara.system.domain.bo.cparams.OrderProduct;
import org.dromara.system.domain.bo.cparams.OrderUpdateBo;
import org.dromara.system.kernel.OrderUpdateKernel;
import org.dromara.system.service.IPayAccountService;
import org.dromara.system.service.IPayOrderLogService;
import org.dromara.system.service.IPayOrderService;
import org.dromara.system.utils.ConversionUtils;
import org.dromara.system.utils.RequestUtils;
import org.dromara.system.utils.UserAgentUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;

@RequiredArgsConstructor
@Service
public class CompanyChannel2Kernel {

    private final IPayOrderLogService orderLogService;
    private final IPayOrderService orderService;
    private final IPayOrderService payOrderService;
    private final OrderUpdateKernel orderUpdateKernel;
    private final IPayAccountService accountService;
    @Value("${gatewayDomain}")
    public String gatewayDomain;

    /**
     * 获取支付链接
     *
     * @param account      收款账号
     * @param groupAccount 轮询组收款账号配置
     * @param order        订单信息
     */
    public R<PayOrder> requestPayLink(PayAccount account, PayGroupAccount groupAccount, PayOrder order, PayOrderLog log) {

        //获取账号请求域名
        String domain = account.getDomain();
        if (StringUtils.isEmpty(domain)) {
            return R.fail("DomainUrl is invalid", order);
        }

        //获取账号配置
        JSONObject config = account.getAccountConfig();
        if (config == null || config.isEmpty() || !config.containsKey("clientId") || !config.containsKey("secretKey") || !config.containsKey("payApi")) {
            return R.fail("Account Config is invalid", order);
        }
        String clientId = config.getString("clientId"); //商户ID
        String secretKey = config.getString("secretKey"); //商户密匙
        String payApi = config.getString("payApi"); //接口API

        //构建参数
        JSONObject params = buildParams(groupAccount, order, clientId, secretKey);
        if (params == null) {
            return R.fail("no config params found", order);
        }

        //请求
        long time = System.currentTimeMillis();
        String res1;
        try {
            res1 = new RequestUtils().doPostJsonNoTry(domain + payApi, params.toJSONString());
        } catch (Exception e) {
            res1 = "请求失败：" + e.getMessage();
        }

        //记录请求日志
        orderLogService.saveLog(log.getLogId(), order.getOrderId(), order.getOrderNo(), order.getOrderAid(), "获取支付链接", params.toString(JSONWriter.Feature.WriteMapNullValue), res1, System.currentTimeMillis() - time);

        try {
            if (StringUtils.isEmpty(res1) || res1.startsWith("请求失败")) {
                return R.fail("Request link null", order);
            }

            //解析返回参数
            JSONObject data;
            try {
                data = JSONObject.parseObject(res1);
            } catch (Exception e) {
                return R.fail(res1, order);
            }
            if(!Objects.equals(data.getInteger("code"), 200)) {
                return R.fail(data.getString("msg"), order);
            }
            if (!data.containsKey("result")) {
                return R.fail("Request link result null", order);
            }
            JSONObject result = data.getJSONObject("result");
            if (result == null || result.isEmpty()) {
                return R.fail("Request link result is empty", order);
            }
            if (!result.containsKey("url")) {
                return R.fail(result.getString("msg"), order);
            }

            String payUrl = result.getString("url");
            String payNo = result.getString("pay_no");

            //写到订单对象里，返出去使用
            order.setLink(payUrl);

            //更新订单
            PayOrder up = new PayOrder();
            up.setOrderId(order.getOrderId());
            up.setLink(payUrl);
            up.setOrderCid(payNo);
            orderService.updatePayOrderAsync(up);

            //记录更新日志
            orderLogService.saveLog(log.getLogId(), order.getOrderId(), order.getOrderNo(), order.getOrderAid(), "更新B端订单信息", JSONObject.toJSONString(up), "success", 10L);

            return R.ok(order);

        } catch (Exception e) {
            return R.fail("Request link fail", order);
        }
    }

    /**
     * 更新订单
     *
     * @param cBo 参数
     * @return JSONObject
     */
    public R<?> updateOrder(CpCh2UpdateBo cBo) {

        if (StringUtils.isEmpty(cBo.getFailure_code()) || StringUtils.isEmpty(cBo.getInvoice_id()) || StringUtils.isEmpty(cBo.getOrder_no())) {
            return R.fail("params is valid");
        }

        Long orderId = Long.parseLong(cBo.getOrder_no().split("_")[0]);

        PayOrder order = payOrderService.selectById(orderId);
        if (order == null) {
            return R.fail("Order does not exist");
        }

        //获取账号配置
        PayAccount account = accountService.selectById(order.getAccountId());
        if (account != null) {
            JSONObject config = account.getAccountConfig();
            if (config.containsKey("secretKey")) {
                String secretKey = config.getString("secretKey");
                String sign = EncryptUtils.encryptBySha256(cBo.getFailure_code() + cBo.getInvoice_id() + cBo.getOrder_no() + secretKey).toUpperCase(Locale.ROOT);
                if (!Objects.equals(sign, cBo.getToken())) {
                    return R.fail("sign is invalid");
                }
            }
        }

        //更新订单参数
        OrderUpdateBo bo = new OrderUpdateBo(orderId, Objects.equals(cBo.getFailure_code(), "success") ? 2 : 3, null);

        //更新订单
        return orderUpdateKernel.updateOrder(order, bo, JSONObject.toJSONString(cBo));
    }

    /**
     * 构建请求参数
     *
     * @param order 订单
     * @return JSONObject
     */
    private JSONObject buildParams(PayGroupAccount groupAccount, PayOrder order, String clientId, String secretKey) {

        String currency; //币别
        BigDecimal amount; //金额
        if (Objects.equals(groupAccount.getIsForce(), 1) && order.getForceCurrency() != null) {
            currency = order.getForceCurrency();
            amount = order.getForceMoney();
        } else {
            currency = order.getCurrency();
            amount = order.getCMoney();
        }

        amount = amount.setScale(2, RoundingMode.HALF_UP);

        AOrderBo.Address bill = JSONObject.parseObject(order.getCustomer(), AOrderBo.Address.class);

        //订单信息
        CpCh2PushBo bo = new CpCh2PushBo();
        bo.setClient_ip(order.getCustomerIp());
        bo.setClient_agent(UserAgentUtils.getUserAgent());
        bo.setClient_language("en");
        bo.setOrder_no(order.getOrderId() + "_" + ConversionUtils.generateShortCode(order.getOrderId() + "", 14));
        bo.setInvoice_id(genInvoiceId());
        bo.setCurrency(currency);
        bo.setSubject(order.getOrderNo());
        bo.setSuccess_uri(order.getSuccessUrl());
        bo.setReturn_uri(order.getCancelUrl());
        bo.setNotify_url(gatewayDomain + "/api/system/external/updateCpCh2");
        bo.setFirst_name(bill.getFirstName());
        bo.setLast_name(bill.getLastName());
        bo.setEmail(order.getCustomerEmail());
        bo.setTelephone(order.getCustomerPhone());
        bo.setAddress(bill.getAddress1());
        bo.setCity(bill.getCity());
        bo.setCountry(bill.getCountry());
        bo.setZip_code(bill.getPostcode());
        bo.setZone(bill.getState());
        bo.setPayment_method("redirect_pay");
        bo.setAmount(amount.toPlainString());
        bo.setUsername(clientId);
        bo.setBn("其它");

        //产品信息
        List<CpCh2PushBo.Goods> prods = new ArrayList<>();
        List<OrderProduct> products = JSONArray.parseArray(order.getProduct(), OrderProduct.class);
        for (OrderProduct product : products) {
            CpCh2PushBo.Goods obj = new CpCh2PushBo.Goods();
            obj.setModel(product.getType());
            obj.setName(product.getName());
            obj.setImage(product.getImage());
            obj.setAttr(product.getAttributes());
            obj.setQty(product.getQuantity());
            obj.setPrice(product.getPrice().toPlainString());
            obj.setLink(product.getImage());
            prods.add(obj);
        }

        bo.setBody(JSONArray.toJSONString(prods));

        //签名
        //$hash_src = '';
        //$hash_key = array('invoice_id', 'order_no');
        //foreach ($hash_key as $key) {
        //    $hash_src .= $data[$key];
        //}
        //$hash_src = $hash_src . $api_sign;
        //$hash = hash('sha256', $hash_src);// sha256 算法
        //$data['token'] = strtoupper($hash);
        //foreach ($data as $k=>$v){
        //    $data[$k] = filter($v);
        //}
        bo.setToken(EncryptUtils.encryptBySha256(bo.getInvoice_id() + bo.getOrder_no() + secretKey).toUpperCase(Locale.ROOT));

        return JSONObject.from(bo, JSONWriter.Feature.WriteMapNullValue);
    }

    public static String genInvoiceId() {
        String token = StringUtils.randomCode(18);
        return md5Hex(UUID.randomUUID().toString()) + token;
    }

    private static String md5Hex(String input) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] messageDigest = md.digest(input.getBytes());
            StringBuilder hexString = new StringBuilder();
            for (byte b : messageDigest) {
                String hex = Integer.toHexString(0xff & b);
                if (hex.length() == 1) hexString.append('0');
                hexString.append(hex);
            }
            return hexString.toString();
        } catch (NoSuchAlgorithmException e) {
            return "";
        }
    }
}
